﻿using ArtMath.Resource;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Drawing.Design;
using ArtMath.Data;
using ArtMath.PointCollection;
using ArtMath.Core.Data;
using ArtMath.Attribute;
using ArtMath.Editor;
using ArtMath.Setting;
using ArtMath.Core.Helper;
using ArtMath.PointCollection.Helper;
using ArtMath.CommonType;

namespace ArtMath.Component.Annotation
{
    /// <summary>
    /// 画笔基类
    /// </summary>
    [Serializable]
    public abstract class PenBase : CustomBase
    {
        protected ColorX penColor;
        PadStringX penWidth;
        [Browsable(false)]
        public List<Curve> Paths { get; } = new List<Curve>();
        SizeD initialSize;
        public PenBase(SketchPad pad, string name, Color color, ToolTypeName toolType, bool addToList = true) : base(pad, name, toolType, addToList)
        {
            ShowBorder = false;
            penColor = new ColorX(Pad, color);
        }
        [MultilingualCategory("Appearance"), MultilingualDisplayName(null, "Brush", "Color")]
        public virtual Color PenColor//设为virtual以供派生类设置默认值
        {
            get { return penColor.Color; }
            set { penColor.Color = value; }
        }
        [MultilingualCategory("Appearance"), Editor(typeof(SliderEditor), typeof(UITypeEditor))]
        public virtual PadStringX PenWidth
        {
            get { return penWidth; }
            set
            {
                if (penWidth is object)
                {
                    penWidth.ExpressionSet -= PenWidth_ExpressionSet;
                }
                penWidth = value;
                if (penWidth is object)
                {
                    penWidth.ExpressionSet += PenWidth_ExpressionSet;
                }
            }
        }
        void PenWidth_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            if (PenWidth.Value < MinWidth)
            {
                Pad.ShowMessage(Resources.Translate(ToolType == ToolTypeName.yzb ? "BallpenSizeLess1" : "HighlighterSizeLess5"), MessageType.Exclamation);
                PenWidth.SetExpression(e.OldExpression, false);
            }
        }
        [DefaultValue(typeof(bool), "False")]
        public override bool ShowBorder
        {
            get
            {
                return base.ShowBorder;
            }

            set
            {
                base.ShowBorder = value;
            }
        }
        public override bool FitSize
        {
            get
            {
                return base.FitSize;
            }

            set
            {
                base.FitSize = value;
                if (FitSize)
                {
                    SizeChanged(initialSize, Size.ToSizeD());
                }
                else
                {
                    SizeChanged(Size.ToSizeD(), initialSize);
                }
            }
        }
        [Browsable(false)]
        public bool IsEmpty
        {
            get
            {
                if (Paths.Count == 0) return true;
                foreach (Curve item in Paths)
                {
                    if (item.Count > 0)
                    {
                        return false;
                    }
                }
                return true;
            }
        }
        public override SizeF ActualSize
        {
            get
            {
                if (!FitSize)
                {
                    return initialSize.ToSizeF();
                }
                return base.ActualSize;
            }
        }
        public override void LocationChanged(PointD oldValue, PointD newValue)
        {
            base.LocationChanged(oldValue, newValue);
            PointD delta = Pad.XYToPxy(newValue) - Pad.XYToPxy(oldValue);
            foreach (Curve cur in Paths)
            {
                for (int i = 0; i < cur.Count; i++)
                {
                    cur[i] += delta;
                }
            }
        }
        public override void CoordinateSystemTypeChanged()
        {
            base.CoordinateSystemTypeChanged();
            PointD offset = PointD.Empty;
            if (Pad.Coordinate == null)
            {
                if (Pad.OldCoordinate != null)
                {
                    offset = Pad.OldCoordinate.OriginCoordinate.ToPointD() / Pad.Zoom.ToPointD();
                }
            }
            else
            {
                if (Pad.OldCoordinate == null)
                {
                    offset = -Pad.Coordinate.OriginCoordinate.ToPointD() / Pad.Zoom.ToPointD();
                }
            }
            foreach (Curve cur in Paths)
            {
                for (int i = 0; i < cur.Count; i++)
                {
                    cur[i] += offset;
                }
            }
        }
        //public override void Move(PointD offset, bool isCompulsive = false, Base sender = null)
        //{
        //    foreach (PaintableCurve cur in Paths)
        //    {
        //        for (int i = 0; i < cur.Samples.Count; i++)
        //        {
        //            cur.Samples[i] += offset;
        //        }
        //    }
        //    base.Move(offset);
        //}
        public override object GetCopyInfo()
        {
            return Paths;
        }
        public override void AutoCreate(object createInfo, bool tryOffset = false)
        {
            IEnumerable<Curve> curs = createInfo as IEnumerable<Curve>;
            if (curs == null) return;
            if (tryOffset)
            {
                List<PointD> pts = new List<PointD>();
                foreach (Curve item in curs)
                {
                    pts.AddRange(item);
                }
                while (Pad.LocationUsed(ToolType, pts))
                {
                    for (int i = 0; i < pts.Count; i++)
                    {
                        pts[i] += new SizeD(Settings.Instance.CopyOffset_WithoutCS_Width, Settings.Instance.CopyOffset_WithoutCS_Height);
                    }
                }
                int count = 0;
                foreach (Curve item in curs)
                {
                    Curve pc = new Curve();
                    int len = count + item.Count;
                    for (int i = count; i < len; i++)
                    {
                        pc.Add(pts[i]);
                    }
                    count = len;
                    Paths.Add(pc);
                }
            }
            else
            {
                foreach (Curve item in curs)
                {
                    Curve pc = new Curve();
                    pc.AddRange(item);
                    Paths.Add(pc);
                }
            }
            Complete();
        }
        public override bool LocationUsed(IList<PointD> locations)
        {
            int i = 0, j = 0, k = 0;
            while (i < Paths.Count && j < Paths[i].Count && k < locations.Count)
            {
                if (Paths[i][j].AlmostEquals(locations[k], false))
                {
                    if (j == Paths[i].Count - 1)
                    {
                        i++;
                        j = 0;
                    }
                    else
                    {
                        j++;
                    }
                    k++;
                }
                else
                {
                    return false;
                }
            }
            return i == Paths.Count && k == locations.Count;
        }
        public override void SizeChanged(SizeD oldValue, SizeD newValue)
        {
            PointD tmp = Pad.XYToPxy(Location.ToPointD());
            foreach (Curve cur in Paths)
            {
                for (int i = 0; i < cur.Count; i++)
                {
                    cur[i] = new PointD(newValue.Width / oldValue.Width * (cur[i].X - tmp.X) + tmp.X,
                        newValue.Height / oldValue.Height * (cur[i].Y - tmp.Y) + tmp.Y);
                }
            }
        }
        public override void RotateAngleChanged(double oldValue)
        {
            //不能有base.xxx
            double angleDelta = Pad.GetRad(oldValue) - RotateAngle.RadValue;
            PointD center = Pad.XYToPxy(RotateCenter.ToPointD()), pmass = Pad.XYToPxy(MassPoint);
            foreach (Curve cur in Paths)
            {
                for (int i = 0; i < cur.Count; i++)
                {
                    cur[i] = cur[i].RotateAt(Settings.Instance.SelfRotate ? center : center - pmass + cur[i], angleDelta);
                }
            }
            setSizeLocation();
        }
        void setSizeLocation()
        {
            RectangleD rect = Utility.GetSmallestSurroundRect(Paths);
            PointD loc = Pad.PxyToMath(rect.Location);
            if (Pad.CoordinateType == CoordinateSystemType.PolarCoordinate)
            {
                PPointS ss = Location as PPointS;
                ss.ρ.SetExpression(loc.X, false);
                ss.θ.SetExpression(loc.Y, false);
            }
            else
            {
                Location.X.SetExpression(loc.X, false);
                Location.Y.SetExpression(loc.Y, false);
            }
            Pxy = rect.Location;
            Size.Width.SetExpression(rect.Width, false);
            Size.Height.SetExpression(rect.Height, false);
            initialSize = rect.Size;
        }
        public void Complete()
        {
            setSizeLocation();
            base.CreateComponents();
        }
        public void Split(int curveIndex, int pointIndex)
        {
            if (curveIndex < 0 || curveIndex >= Paths.Count) return;
            if (pointIndex < 0 || pointIndex >= Paths[curveIndex].Count) return;
            Paths[curveIndex].RemoveAt(pointIndex);
            if (Paths[curveIndex].Count == 0)
            {
                Paths.RemoveAt(curveIndex);
                return;
            }
            if (pointIndex > 0 && pointIndex < Paths[curveIndex].Count)//不能-1，因为已经移除了1个元素
            {
                Curve cur = new Curve();
                for (int i = pointIndex; i < Paths[curveIndex].Count; i++)
                {
                    cur.Add(Paths[curveIndex][i]);
                }
                Paths[curveIndex].RemoveRange(pointIndex, Paths[curveIndex].Count - pointIndex);
                Paths.Insert(curveIndex + 1, cur);
            }
        }
        public override void ComponentBuilding(Base component)
        {
            if (Paths.Count > 0)
            {
                Paths[Paths.Count - 1].Add(SpareCoordinate);
            }
        }
        public override Base[] CheckNearBy(PointD Plocation)
        {
            double dist = Settings.Instance.LineAbsorb + Pad.PointSelectorRadius + PenWidth.Value / 2;
            foreach (Curve cur in Paths)
            {
                for (int i = 0; i < cur.Count - 1; i++)
                {
                    if (new PointCollectionBase[] { new LineSegment(cur[i], cur[i + 1]) }.GetNearestPoint(Plocation).Value.DistanceTo(Plocation) <= dist)
                    {
                        return new Base[] { this };
                    }
                }
            }
            return new Base[0];
        }
        public override void CreateComponents()
        {
            Paths.Add(new Curve());
            Pad.TrackObj = this;
        }
        public override void CheckInRegion(RectangleD rect)
        {
            SetInPicker(rect.IntersectCurves(Paths));
        }
        protected abstract float MinWidth { get; }
        public override void Paint(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible)
            {
                Pen p = new Pen(StyleState_ == StyleState.Hover || Selected && Pad.UseReversedColor ? penColor.ReversedColor : penColor.Color, Math.Max(penWidth.FloatValue, MinWidth)) { LineJoin = LineJoin.Round };
                foreach (Curve item in Paths)
                {
                    item.Paint(gra, p);
                }
                if (ShowBorder)//画笔类的边框不需要旋转后绘制，所以边框在此绘制而不用base.Paint(gra);
                {
                    RectangleD rect = DrawRegion;
                    Pen P = new Pen(borderColor.Color, BorderThickness.FloatValue)
                    {
                        DashStyle = BorderDashStyle
                    };
                    gra.DrawRectangle(P, (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height);
                }
            }
            //base.Paint(gra);
        }
        public override void UseFormat(Base obj)
        {
            base.UseFormat(obj);
            if (Settings.Instance.LayoutUseFormat)
            {
                CustomBase cb = obj as CustomBase;
                if (cb == null) return;
                FitSize = cb.FitSize;//FitSize必须最后设置，否则请尝试以下操作：画线，改变尺寸，FitSize=False，复制粘贴。副本尺寸错误
            }
        }
    }
}
